package com.axinfu.util.encrypt;

import com.axinfu.util.Constant;
import com.axinfu.util.EmptyUtil;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * 基础加密组件
 * <pre>
 * BASE64加密解密
 * MD5单向加密
 * SHA单向加密
 * HMAC单向带秘钥加密
 * DES加密解密
 * 3DES加密解密
 * AES加密解密
 * </pre>
 * 算法名称/加密模式/填充方式
 * <pre>
 * ECB：电码本模式
 * CBC：密码分组链接模式
 * CTR：计算器模式
 * CFB：密码反馈模式
 * OFB：输出反馈模式
 * </pre>
 * <pre>
 * NoPadding
 * PKCS5Padding
 * PKCS7Padding
 * ISO10126Padding
 * </pre>
 *
 * @author zjn
 * @since 2022/3/23
 */
@SuppressWarnings("unused")
public class Encryption {

    /**
     * 编码
     */
    public static final String ENCODING_UTF8 = "UTF-8";
    public static final String ENCODING_GBK = "GBK";

    /**
     * 加密模式
     */
    public static final String ENCRYPT_MODE_ECB = "ECB";
    public static final String ENCRYPT_MODE_CBC = "CBC";
    public static final String ENCRYPT_MODE_CTR = "CTR";
    public static final String ENCRYPT_MODE_CFB = "CFB";
    public static final String ENCRYPT_MODE_OFB = "OFB";

    /**
     * 填充方式
     */
    public static final String ENCRYPT_PADDING_NOPADDING = "NoPadding";
    public static final String ENCRYPT_PADDING_PKCS5PADDING = "PKCS5Padding";
    public static final String ENCRYPT_PADDING_PKCS7PADDING = "PKCS7Padding";
    public static final String ENCRYPT_PADDING_ISO10126PADDING = "ISO10126Padding";

    private Encryption() {
    }

    public static class EncryptionKey {
        private final byte[] key;
        private final String keyHex;
        private final String keyBase64;

        public EncryptionKey(byte[] key) {
            this.key = key;
            this.keyHex = Encryption.bytesToHex(key);
            this.keyBase64 = Encryption.encryptBase64ToString(key);
        }

        public byte[] getKey() {
            return key;
        }

        public String getKeyHex() {
            return keyHex;
        }

        public String getKeyBase64() {
            return keyBase64;
        }
    }

    // //////////////////////////////////////////////////////////
    // BASE64加密解密
    // //////////////////////////////////////////////////////////

    /**
     * BASE64加密
     *
     * @param data 需要加密的字节数组
     * @return 加密后的字节数组
     */
    public static byte[] encryptBase64(byte[] data) {
        return Base64.getEncoder().encode(data);
    }

    /**
     * BASE64加密(采用默认编码UTF-8)
     *
     * @param data     需要加密的字符串
     * @param encoding 字符编码
     * @return 加密后的字节数组
     */
    public static byte[] encryptBase64(String data, String encoding) {
        try {
            return encryptBase64(data.getBytes(encoding));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * BASE64加密(采用默认编码UTF-8)
     *
     * @param data 需要加密的字符串
     * @return 加密后的字节数组
     */
    public static byte[] encryptBase64(String data) {
        return encryptBase64(data, ENCODING_UTF8);
    }

    /**
     * BASE64加密
     *
     * @param data 需要加密的字节数组
     * @return 加密后的字符串
     */
    public static String encryptBase64ToString(byte[] data) {
        return Base64.getEncoder().encodeToString(data);
    }

    /**
     * BASE64加密(采用默认编码UTF-8)
     *
     * @param data     需要加密的字符串
     * @param encoding 字符编码
     * @return 加密后的字符串
     */
    public static String encryptBase64ToString(String data, String encoding) {
        try {
            return encryptBase64ToString(data.getBytes(encoding));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * BASE64加密(采用默认编码UTF-8)
     *
     * @param data 需要加密的字符串
     * @return 加密后的字符串
     */
    public static String encryptBase64ToString(String data) {
        return encryptBase64ToString(data, ENCODING_UTF8);
    }

    /**
     * BASE64解密
     *
     * @param data 需要解密的加密字节数组
     * @return 解密后的原始字节数组
     */
    public static byte[] decryptBase64(byte[] data) {
        return Base64.getDecoder().decode(data);
    }

    /**
     * BASE64解密
     *
     * @param data     需要解密的加密字符串
     * @param encoding 字符编码
     * @return 解密后的原始字节数组
     */
    public static byte[] decryptBase64(String data, String encoding) {
        try {
            return decryptBase64(data.getBytes(encoding));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * BASE64解密
     *
     * @param data 需要解密的加密字符串
     * @return 解密后的原始字节数组
     */
    public static byte[] decryptBase64(String data) {
        return decryptBase64(data, Encryption.ENCODING_UTF8);
    }

    /**
     * BASE64解密
     *
     * @param data           需要解密的加密字符串
     * @param encoding       字符编码
     * @param stringEncoding 字符串编码
     * @return 解密后的原始字符串
     */
    public static String decryptBase64ToString(String data, String encoding, String stringEncoding) {
        try {
            return new String(decryptBase64(data.getBytes(encoding)), stringEncoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * BASE64解密
     *
     * @param data           需要解密的加密字符串
     * @param stringEncoding 字符串编码
     * @return 解密后的原始字符串
     */
    public static String decryptBase64ToString(String data, String stringEncoding) {
        return decryptBase64ToString(data, Encryption.ENCODING_UTF8, stringEncoding);
    }

    /**
     * BASE64解密
     *
     * @param data 需要解密的加密字符串
     * @return 解密后的原始字符串
     */
    public static String decryptBase64ToString(String data) {
        return decryptBase64ToString(data, Encryption.ENCODING_UTF8);
    }

    // //////////////////////////////////////////////////////////
    // MD5加密
    // //////////////////////////////////////////////////////////

    public static final String ALGORITHM_MD5 = "MD5";

    /**
     * MD5加密
     *
     * @param data 需要加密的数据
     * @return 加密后的数据
     */
    public static byte[] encryptMd5(byte[] data) {
        try {
            MessageDigest m5 = MessageDigest.getInstance(ALGORITHM_MD5);
            m5.update(data);
            return m5.digest();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * MD5加密
     *
     * @param data 需要加密的数据
     * @return 加密后的数据
     */
    public static String encryptMd5ToHex(byte[] data) {
        return bytesToHex(encryptMd5(data));
    }

    /**
     * MD5加密
     *
     * @param data     需要加密的数据
     * @param encoding 需要加密的数据的解析编码
     * @return 加密后的数据
     */
    public static String encryptMd5ToHex(String data, String encoding) {
        try {
            return encryptMd5ToHex(data.getBytes(encoding));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * MD5加密
     *
     * @param data 需要加密的数据
     * @return 加密后的数据
     */
    public static String encryptMd5ToHex(String data) {
        return encryptMd5ToHex(data, ENCODING_UTF8);
    }

    /**
     * MD5加密
     *
     * @param data 需要加密的数据
     * @return 加密后的数据
     */
    public static String encryptMd5ToBase64(byte[] data) {
        return encryptBase64ToString(encryptMd5(data));
    }

    /**
     * MD5加密
     *
     * @param data     需要加密的数据
     * @param encoding 需要加密的数据的解析编码
     * @return 加密后的数据
     */
    public static String encryptMd5ToBase64(String data, String encoding) {
        try {
            return encryptMd5ToBase64(data.getBytes(encoding));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * MD5加密
     *
     * @param data 需要加密的数据
     * @return 加密后的数据
     */
    public static String encryptMd5ToBase64(String data) {
        return encryptMd5ToBase64(data, ENCODING_UTF8);
    }

    // //////////////////////////////////////////////////////////
    // SHA加密
    // //////////////////////////////////////////////////////////

    public static final String ALGORITHM_SHA = "SHA";

    public static final String ALGORITHM_SHA_BIT_1 = "1";
    public static final String ALGORITHM_SHA_BIT_256 = "256";
    public static final String ALGORITHM_SHA_BIT_384 = "384";
    public static final String ALGORITHM_SHA_BIT_512 = "512";

    /**
     * SHA加密
     *
     * @param data 需要加密的数据
     * @param bit  输出位数
     * @return 加密后的数据
     */
    public static byte[] encryptSha(String bit, byte[] data) {
        try {
            String algorithmShaBit = ALGORITHM_SHA + "-" + bit;
            MessageDigest md = MessageDigest.getInstance(algorithmShaBit);
            md.update(data);
            return md.digest();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * SHA加密
     *
     * @param data 需要加密的数据
     * @param bit  输出位数
     * @return 加密后的数据
     */
    public static String encryptShaToHex(String bit, byte[] data) {
        return bytesToHex(encryptSha(bit, data));
    }

    /**
     * SHA加密
     *
     * @param data     需要加密的数据
     * @param bit      输出位数
     * @param encoding 需要加密的数据的解析编码
     * @return 加密后的数据
     */
    public static String encryptShaToHex(String bit, String data, String encoding) {
        try {
            return encryptShaToHex(bit, data.getBytes(encoding));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * SHA加密
     *
     * @param data 需要加密的数据
     * @param bit  输出位数
     * @return 加密后的数据
     */
    public static String encryptShaToHex(String bit, String data) {
        return encryptShaToHex(bit, data, ENCODING_UTF8);
    }

    /**
     * SHA加密
     *
     * @param data 需要加密的数据
     * @param bit  输出位数
     * @return 加密后的数据
     */
    public static String encryptShaToBase64(String bit, byte[] data) {
        return encryptBase64ToString(encryptSha(bit, data));
    }

    /**
     * SHA加密
     *
     * @param data     需要加密的数据
     * @param bit      输出位数
     * @param encoding 需要加密的数据的解析编码
     * @return 加密后的数据
     */
    public static String encryptShaToBase64(String bit, String data, String encoding) {
        try {
            return encryptShaToBase64(bit, data.getBytes(encoding));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * SHA加密
     *
     * @param data 需要加密的数据
     * @param bit  输出位数
     * @return 加密后的数据
     */
    public static String encryptShaToBase64(String bit, String data) {
        return encryptShaToBase64(bit, data, ENCODING_UTF8);
    }

    // //////////////////////////////////////////////////////////
    // HMAC带秘钥加密
    // //////////////////////////////////////////////////////////

    public static final String ALGORITHM_HMAC = "HMAC";
    /**
     * 获取秘钥，MAC算法可选以下多种算法
     * <pre>
     * HmacMD5
     * HmacSHA1
     * HmacSHA256
     * HmacSHA384
     * HmacSHA512
     * </pre>
     */
    public static final String HAMC_ALGORITHM_MD5 = "MD5";
    public static final String HAMC_ALGORITHM_SHA1 = "SHA1";
    public static final String HAMC_ALGORITHM_SHA256 = "SHA256";
    public static final String HAMC_ALGORITHM_SHA384 = "SHA384";
    public static final String HAMC_ALGORITHM_SHA512 = "SHA512";

    /**
     * 生成HMAC秘钥
     *
     * @param keySize  秘钥位数
     * @param hashName 秘钥hash算法
     * @param seed     种子
     * @return HMAC秘钥
     */
    public static EncryptionKey generatorHmacKey(int keySize, String hashName, byte[] seed) {
        String algorithmHmacKey = ALGORITHM_HMAC + hashName;
        return generatorKey(algorithmHmacKey, keySize, seed);
    }

    /**
     * 生成HMAC秘钥
     *
     * @param keySize  秘钥位数
     * @param hashName 秘钥hash算法
     * @return HMAC秘钥
     */
    public static EncryptionKey generatorHmacKey(int keySize, String hashName) {
        return generatorHmacKey(keySize, hashName, null);
    }

    /**
     * HMAC加密(指定秘钥算法)
     *
     * @param hashName 秘钥hash算法
     * @param data     需要加密的数据
     * @param key      秘钥
     * @return 加密后的数据
     */
    public static byte[] encryptHmac(String hashName, byte[] data, byte[] key) {
        try {
            String algorithmHmacKey = ALGORITHM_HMAC + hashName;
            SecretKey secretKey = new SecretKeySpec(key, algorithmHmacKey);
            Mac mac = Mac.getInstance(algorithmHmacKey);
            mac.init(secretKey);
            return mac.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * HMAC加密
     *
     * @param hashName 秘钥hash算法
     * @param data     需要加密的字符串
     * @param key      秘钥
     * @return 加密后的字符串
     */
    public static String encryptHmacToHex(String hashName, byte[] data, byte[] key) {
        return bytesToHex(encryptHmac(hashName, data, key));
    }

    /**
     * HMAC加密
     *
     * @param data     需要加密的字符串
     * @param key      秘钥
     * @param hashName 秘钥hash算法
     * @param encoding 需要加密的数据和秘钥的解析编码
     * @return 加密后的字符串
     */
    public static String encryptHmacToHex(String hashName, String data, byte[] key, String encoding) {
        try {
            return encryptHmacToHex(hashName, data.getBytes(encoding), key);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * HMAC加密
     *
     * @param hashName 秘钥hash算法
     * @param data     需要加密的字符串
     * @param key      秘钥
     * @return 加密后的字符串
     */
    public static String encryptHmacToHex(String hashName, String data, byte[] key) {
        return encryptHmacToHex(hashName, data, key, ENCODING_UTF8);
    }

    /**
     * HMAC加密
     *
     * @param hashName 秘钥hash算法
     * @param data     需要加密的字符串
     * @param key      秘钥
     * @return 加密后的字符串
     */
    public static String encryptHmacToBase64(String hashName, byte[] data, byte[] key) {
        return encryptBase64ToString(encryptHmac(hashName, data, key));
    }

    /**
     * HMAC加密
     *
     * @param data     需要加密的字符串
     * @param key      秘钥
     * @param hashName 秘钥hash算法
     * @param encoding 需要加密的数据和秘钥的解析编码
     * @return 加密后的字符串
     */
    public static String encryptHmacToBase64(String hashName, String data, byte[] key, String encoding) {
        try {
            return encryptHmacToBase64(hashName, data.getBytes(encoding), key);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * HMAC加密
     *
     * @param hashName 秘钥hash算法
     * @param data     需要加密的字符串
     * @param key      秘钥
     * @return 加密后的字符串
     */
    public static String encryptHmacToBase64(String hashName, String data, byte[] key) {
        return encryptHmacToBase64(hashName, data, key, ENCODING_UTF8);
    }

    // //////////////////////////////////////////////////////////
    // DES加密解密
    // //////////////////////////////////////////////////////////

    public static final String ALGORITHM_DES = "DES";

    /**
     * 生成DES秘钥
     *
     * @param keySize 秘钥位数
     * @param seed    种子
     * @return DES秘钥
     */
    public static EncryptionKey generatorDesKey(int keySize, byte[] seed) {
        return generatorKey(ALGORITHM_DES, keySize, seed);
    }

    /**
     * 生成DES秘钥
     *
     * @param keySize 秘钥位数
     * @return DES秘钥
     */
    public static EncryptionKey generatorDesKey(int keySize) {
        return generatorDesKey(keySize, null);
    }

    /**
     * DES加密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     密钥
     * @param iv      向量
     * @return 加密后的数据
     */
    public static byte[] encryptDes(String mode, String padding, byte[] data, byte[] key, byte[] iv) {
        try {
            String cipherAlgorithm = ALGORITHM_DES + "/" + mode + "/" + padding;
            DESKeySpec desKey = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_DES);
            Key deskey = keyFactory.generateSecret(desKey);
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            if (EmptyUtil.isNotEmpty(iv)) {
                IvParameterSpec ips = new IvParameterSpec(iv);
                cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
            } else {
                cipher.init(Cipher.ENCRYPT_MODE, deskey);
            }
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DES加密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     base64编码的密钥
     * @param iv      向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesToHex(String mode, String padding, byte[] data, byte[] key, byte[] iv) {
        return bytesToHex(encryptDes(mode, padding, data, key, iv));
    }

    /**
     * DES加密
     *
     * @param mode     模式
     * @param padding  填充方式
     * @param data     待加密数据
     * @param key      base64编码的密钥
     * @param iv       向量
     * @param encoding 需要加密的数据和向量的解析编码
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesToHex(String mode, String padding, String data, byte[] key, byte[] iv,
                                         String encoding) {
        try {
            return encryptDesToHex(mode, padding, data.getBytes(encoding), key, iv);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DES加密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     base64编码的密钥
     * @param iv      向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesToHex(String mode, String padding, String data, byte[] key, byte[] iv) {
        return encryptDesToHex(mode, padding, data, key, iv, ENCODING_UTF8);
    }

    /**
     * DES加密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     base64编码的密钥
     * @param iv      向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesToBase64(String mode, String padding, byte[] data, byte[] key, byte[] iv) {
        return encryptBase64ToString(encryptDes(mode, padding, data, key, iv));
    }

    /**
     * DES加密
     *
     * @param mode     模式
     * @param padding  填充方式
     * @param data     待加密数据
     * @param key      base64编码的密钥
     * @param iv       向量
     * @param encoding 需要加密的数据和向量的解析编码
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesToBase64(String mode, String padding, String data, byte[] key, byte[] iv,
                                            String encoding) {
        try {
            return encryptDesToBase64(mode, padding, data.getBytes(encoding), key, iv);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DES加密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     base64编码的密钥
     * @param iv      向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesToBase64(String mode, String padding, String data, byte[] key, byte[] iv) {
        return encryptDesToBase64(mode, padding, data, key, iv, ENCODING_UTF8);
    }

    /**
     * DES解密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     密钥
     * @param iv      向量
     * @return 解密后的数据
     */
    public static byte[] decryptDes(String mode, String padding, byte[] data, byte[] key, byte[] iv) {
        try {
            String cipherAlgorithm = ALGORITHM_DES + "/" + mode + "/" + padding;
            DESKeySpec desKey = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_DES);
            Key deskey = keyFactory.generateSecret(desKey);
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            if (EmptyUtil.isNotEmpty(iv)) {
                IvParameterSpec ips = new IvParameterSpec(iv);
                cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
            } else {
                cipher.init(Cipher.DECRYPT_MODE, deskey);
            }
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DES解密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     密钥
     * @param iv      向量
     * @return 解密后的原始字符串
     */
    public static byte[] decryptDesFromHex(String mode, String padding, String data, byte[] key, byte[] iv) {
        return decryptDes(mode, padding, hexToBytes(data), key, iv);
    }

    /**
     * DES解密
     *
     * @param mode     模式
     * @param padding  填充方式
     * @param data     待加密数据
     * @param key      密钥
     * @param iv       向量
     * @param encoding 解密后字符编码
     * @return 解密后的原始字符串
     */
    public static String decryptDesFromHexToString(String mode, String padding, String data, byte[] key, byte[] iv,
                                                   String encoding) {
        try {
            return new String(decryptDesFromHex(mode, padding, data, key, iv), encoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DES解密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     密钥
     * @param iv      向量
     * @return 解密后的原始字符串
     */
    public static String decryptDesFromHexToString(String mode, String padding, String data, byte[] key, byte[] iv) {
        return decryptDesFromHexToString(mode, padding, data, key, iv, ENCODING_UTF8);
    }

    /**
     * DES解密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     密钥
     * @param iv      向量
     * @return 解密后的原始字符串
     */
    public static byte[] decryptDesFromBase64(String mode, String padding, String data, byte[] key, byte[] iv) {
        return decryptDes(mode, padding, decryptBase64(data), key, iv);
    }

    /**
     * DES解密
     *
     * @param mode     模式
     * @param padding  填充方式
     * @param data     待加密数据
     * @param key      密钥
     * @param iv       向量
     * @param encoding 解密后字符编码
     * @return 解密后的原始字符串
     */
    public static String decryptDesFromBase64ToString(String mode, String padding, String data, byte[] key, byte[] iv
            , String encoding) {
        try {
            return new String(decryptDesFromBase64(mode, padding, data, key, iv), encoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DES解密
     *
     * @param mode    模式
     * @param padding 填充方式
     * @param data    待加密数据
     * @param key     密钥
     * @param iv      向量
     * @return 解密后的原始字符串
     */
    public static String decryptDesFromBase64ToString(String mode, String padding, String data, byte[] key, byte[] iv) {
        return decryptDesFromBase64ToString(mode, padding, data, key, iv, ENCODING_UTF8);
    }

    //// //////////////////////////////////////////////////////////
    //// DESede加密解密
    //// //////////////////////////////////////////////////////////

    public static final String ALGORITHM_DESEDE = "DESede";

    /**
     * 生成DESede秘钥
     *
     * @param keySize 秘钥位数
     * @param seed    种子
     * @return DESede秘钥
     */
    public static EncryptionKey generatorDesedeKey(int keySize, byte[] seed) {
        return generatorKey(ALGORITHM_DESEDE, keySize, seed);
    }

    /**
     * 生成DESede秘钥
     *
     * @param keySize 秘钥位数
     * @return DESede秘钥
     */
    public static EncryptionKey generatorDesedeKey(int keySize) {
        return generatorDesedeKey(keySize, null);
    }

    /**
     * DESede加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 加密后的数据
     */
    public static byte[] encryptDesede(String algorithm, String padding, byte[] data, byte[] key, byte[] iv) {
        try {
            String cipherAlgorithm = ALGORITHM_DESEDE + "/" + algorithm + "/" + padding;
            DESedeKeySpec desKey = new DESedeKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_DESEDE);
            Key deskey = keyFactory.generateSecret(desKey);
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            if (EmptyUtil.isNotEmpty(iv)) {
                IvParameterSpec ips = new IvParameterSpec(iv);
                cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
            } else {
                cipher.init(Cipher.ENCRYPT_MODE, deskey);
            }
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DESede加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesedeToHex(String algorithm, String padding, byte[] data, byte[] key, byte[] iv) {
        return bytesToHex(encryptDesede(algorithm, padding, data, key, iv));
    }

    /**
     * DESede加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @param encoding  需要加密的数据和向量的解析编码
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesedeToHex(String algorithm, String padding, String data, byte[] key, byte[] iv,
                                            String encoding) {
        try {
            return encryptDesedeToHex(algorithm, padding, data.getBytes(encoding), key, iv);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DESede加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesedeToHex(String algorithm, String padding, String data, byte[] key, byte[] iv) {
        return encryptDesedeToHex(algorithm, padding, data, key, iv, ENCODING_UTF8);
    }

    /**
     * DESede加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesedeToBase64(String algorithm, String padding, byte[] data, byte[] key, byte[] iv) {
        return encryptBase64ToString(encryptDesede(algorithm, padding, data, key, iv));
    }

    /**
     * DESede加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @param encoding  需要加密的数据和向量的解析编码
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesedeToBase64(String algorithm, String padding, String data, byte[] key, byte[] iv,
                                               String encoding) {
        try {
            return encryptDesedeToBase64(algorithm, padding, data.getBytes(encoding), key, iv);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DESede加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptDesedeToBase64(String algorithm, String padding, String data, byte[] key, byte[] iv) {
        return encryptDesedeToBase64(algorithm, padding, data, key, iv, ENCODING_UTF8);
    }

    /**
     * DESede解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的数据
     */
    public static byte[] decryptDesede(String algorithm, String padding, byte[] data, byte[] key, byte[] iv) {
        try {
            String cipherAlgorithm = ALGORITHM_DESEDE + "/" + algorithm + "/" + padding;
            DESedeKeySpec desKey = new DESedeKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM_DESEDE);
            Key deskey = keyFactory.generateSecret(desKey);
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            if (EmptyUtil.isNotEmpty(iv)) {
                IvParameterSpec ips = new IvParameterSpec(iv);
                cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
            } else {
                cipher.init(Cipher.DECRYPT_MODE, deskey);
            }
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DESede解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的原始字符串
     */
    public static byte[] decryptDesedeFromHex(String algorithm, String padding, String data, byte[] key, byte[] iv) {
        return decryptDesede(algorithm, padding, hexToBytes(data), key, iv);
    }

    /**
     * DESede解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @param encoding  解密后字符编码
     * @return 解密后的原始字符串
     */
    public static String decryptDesedeFromHexToString(String algorithm, String padding, String data, byte[] key,
                                                      byte[] iv, String encoding) {
        try {
            return new String(decryptDesedeFromHex(algorithm, padding, data, key, iv), encoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DESede解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的原始字符串
     */
    public static String decryptDesedeFromHexToString(String algorithm, String padding, String data, byte[] key,
                                                      byte[] iv) {
        return decryptDesedeFromHexToString(algorithm, padding, data, key, iv, ENCODING_UTF8);
    }

    /**
     * DESede解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的原始字符串
     */
    public static byte[] decryptDesedeFromBase64(String algorithm, String padding, String data, byte[] key, byte[] iv) {
        return decryptDesede(algorithm, padding, decryptBase64(data), key, iv);
    }

    /**
     * DESede解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @param encoding  解密后字符编码
     * @return 解密后的原始字符串
     */
    public static String decryptDesedeFromBase64ToString(String algorithm, String padding, String data, byte[] key,
                                                         byte[] iv, String encoding) {
        try {
            return new String(decryptDesedeFromBase64(algorithm, padding, data, key, iv), encoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * DESede解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的原始字符串
     */
    public static String decryptDesedeFromBase64ToString(String algorithm, String padding, String data, byte[] key,
                                                         byte[] iv) {
        return decryptDesedeFromBase64ToString(algorithm, padding, data, key, iv, ENCODING_UTF8);
    }

    // //////////////////////////////////////////////////////////
    // AES带秘钥加密解密
    // //////////////////////////////////////////////////////////

    private static final String ALGORITHM_AES = "AES";

    /**
     * 生成AES秘钥
     *
     * @param seed    种子
     * @param keySize 秘钥长度
     * @return AES秘钥
     */
    public static EncryptionKey generatorAesKey(int keySize, byte[] seed) {
        return generatorKey(ALGORITHM_AES, keySize, seed);
    }

    /**
     * 生成AES秘钥
     *
     * @param keySize 秘钥长度
     * @return AES秘钥
     */
    public static EncryptionKey generatorAesKey(int keySize) {
        return generatorAesKey(keySize, null);
    }

    /**
     * AES加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 加密后的数据
     */
    public static byte[] encryptAes(String algorithm, String padding, byte[] data, byte[] key, byte[] iv) {
        try {
            String cipherAlgorithm = ALGORITHM_AES + "/" + algorithm + "/" + padding;
            SecretKey secretKey = new SecretKeySpec(key, ALGORITHM_AES);
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            if (EmptyUtil.isNotEmpty(iv)) {
                IvParameterSpec ips = new IvParameterSpec(iv);
                cipher.init(Cipher.ENCRYPT_MODE, secretKey, ips);
            } else {
                cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            }
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * AES加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptAesToHex(String algorithm, String padding, byte[] data, byte[] key, byte[] iv) {
        return bytesToHex(encryptAes(algorithm, padding, data, key, iv));
    }

    /**
     * AES加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @param encoding  需要加密的数据和向量的解析编码
     * @return 加密后的base64编码字符串
     */
    public static String encryptAesToHex(String algorithm, String padding, String data, byte[] key, byte[] iv,
                                         String encoding) {
        try {
            return encryptAesToHex(algorithm, padding, data.getBytes(encoding), key, iv);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * AES加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptAesToHex(String algorithm, String padding, String data, byte[] key, byte[] iv) {
        return encryptAesToHex(algorithm, padding, data, key, iv, ENCODING_UTF8);
    }

    /**
     * AES加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptAesToBase64(String algorithm, String padding, byte[] data, byte[] key, byte[] iv) {
        return encryptBase64ToString(encryptAes(algorithm, padding, data, key, iv));
    }

    /**
     * AES加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @param encoding  需要加密的数据和向量的解析编码
     * @return 加密后的base64编码字符串
     */
    public static String encryptAesToBase64(String algorithm, String padding, String data, byte[] key, byte[] iv,
                                            String encoding) {
        try {
            return encryptAesToBase64(algorithm, padding, data.getBytes(encoding), key, iv);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * AES加密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       base64编码的密钥
     * @param iv        向量
     * @return 加密后的base64编码字符串
     */
    public static String encryptAesToBase64(String algorithm, String padding, String data, byte[] key, byte[] iv) {
        return encryptAesToBase64(algorithm, padding, data, key, iv, ENCODING_UTF8);
    }

    /**
     * AES解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的数据
     */
    public static byte[] decryptAes(String algorithm, String padding, byte[] data, byte[] key, byte[] iv) {
        try {
            String cipherAlgorithm = ALGORITHM_AES + "/" + algorithm + "/" + padding;
            SecretKey secretKey = new SecretKeySpec(key, ALGORITHM_AES);
            Cipher cipher = Cipher.getInstance(cipherAlgorithm);
            if (EmptyUtil.isNotEmpty(iv)) {
                IvParameterSpec ips = new IvParameterSpec(iv);
                cipher.init(Cipher.DECRYPT_MODE, secretKey, ips);
            } else {
                cipher.init(Cipher.DECRYPT_MODE, secretKey);
            }
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * AES解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的原始字符串
     */
    public static byte[] decryptAesFromHex(String algorithm, String padding, String data, byte[] key, byte[] iv) {
        return decryptAes(algorithm, padding, hexToBytes(data), key, iv);
    }

    /**
     * AES解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @param encoding  解密后字符编码
     * @return 解密后的原始字符串
     */
    public static String decryptAesFromHexToString(String algorithm, String padding, String data, byte[] key,
                                                   byte[] iv, String encoding) {
        try {
            return new String(decryptAesFromHex(algorithm, padding, data, key, iv), encoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * AES解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的原始字符串
     */
    public static String decryptAesFromHexToString(String algorithm, String padding, String data, byte[] key,
                                                   byte[] iv) {
        return decryptAesFromHexToString(algorithm, padding, data, key, iv, ENCODING_UTF8);
    }

    /**
     * AES解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的原始字符串
     */
    public static byte[] decryptAesFromBase64(String algorithm, String padding, String data, byte[] key, byte[] iv) {
        return decryptAes(algorithm, padding, decryptBase64(data), key, iv);
    }

    /**
     * AES解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @param encoding  解密后字符编码
     * @return 解密后的原始字符串
     */
    public static String decryptAesFromBase64ToString(String algorithm, String padding, String data, byte[] key,
                                                      byte[] iv, String encoding) {
        try {
            return new String(decryptAesFromBase64(algorithm, padding, data, key, iv), encoding);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * AES解密
     *
     * @param algorithm 算法
     * @param padding   填充方式
     * @param data      待加密数据
     * @param key       密钥
     * @param iv        向量
     * @return 解密后的原始字符串
     */
    public static String decryptAesFromBase64ToString(String algorithm, String padding, String data, byte[] key,
                                                      byte[] iv) {
        return decryptAesFromBase64ToString(algorithm, padding, data, key, iv, ENCODING_UTF8);
    }

    // //////////////////////////////////////////////////////////
    // 十六进制转换
    // //////////////////////////////////////////////////////////

    private final static byte[] HEX_BYTES = "0123456789ABCDEF".getBytes();

    /**
     * 将byte[]转换为十六进制进制字符串
     *
     * @param bytes byte数组
     * @return 十六进制进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        int l = bytes.length;
        byte[] buff = new byte[2 * l];
        for (int i = 0; i < l; i++) {
            buff[2 * i] = HEX_BYTES[(bytes[i] >> 4) & 0x0f];
            buff[2 * i + 1] = HEX_BYTES[bytes[i] & 0x0f];
        }
        return new String(buff);
    }

    /**
     * 将十六进制进制字符串转换为byte[]
     *
     * @param hexstr byte数组
     * @return 十六进制进制字符串
     */
    public static byte[] hexToBytes(String hexstr) {
        byte[] b = new byte[hexstr.length() / 2];
        int k = 0;
        for (int i = 0, l = b.length; i < l; i++) {
            char c0 = hexstr.charAt(k++);
            char c1 = hexstr.charAt(k++);
            b[i] = (byte) ((parse(c0) << 4) | parse(c1));
        }
        return b;
    }

    // //////////////////////////////////////////////////////////
    // 其他
    // //////////////////////////////////////////////////////////

    /**
     * 生成key
     *
     * @param algorithmAes algorithmAes
     * @param keySize      keySize
     * @param seed         seed
     * @return EncryptionKey
     */
    public static EncryptionKey generatorKey(String algorithmAes, int keySize, byte[] seed) {
        try {
            KeyGenerator kg = KeyGenerator.getInstance(algorithmAes);
            SecureRandom secureRandom;
            if (EmptyUtil.isNotEmpty(seed)) {
                secureRandom = new SecureRandom(seed);
            } else {
                secureRandom = new SecureRandom();
            }
            kg.init(keySize, secureRandom);
            SecretKey secretKey = kg.generateKey();
            return new EncryptionKey(secretKey.getEncoded());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将字符转换为整数
     *
     * @param c 需要计算的字符
     * @return 计算后的整数
     */
    private static int parse(char c) {
        if (c >= Constant.CHAR_LOWER_A) {
            return (c - Constant.CHAR_LOWER_A + 10) & 0x0f;
        }
        if (c >= Constant.CHAR_UPPER_A) {
            return (c - Constant.CHAR_UPPER_A + 10) & 0x0f;
        }
        return (c - Constant.CHAR_0) & 0x0f;
    }
}
